#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Datetime: 2022/1/12 13:53
# @Author  : CHENWang
# @Site    : 
# @File    : coingecko.py
# @Software: PyCharm

"""
脚本说明:
API documentation
https://www.coingecko.com/api/docs/v3
https://www.coingecko.com/en/api/documentation
Our Free API* has a rate limit of 50 calls/minute.
"""

import pandas as pd
import time
import json
import requests

from requests.adapters import HTTPAdapter
from urllib3.util import Retry
from tools.wrappers import deco_retry
from tools.time_tool import str_to_timestamp

fiat_crypto = {'btc': 'bitcoin',
               'eth': 'ethereum',
               'ltc': 'litecoin',
               'bch': 'bitcoin-cash',
               'bnb': 'binancecoin',
               'eos': 'eos',
               'xrp': 'ripple',
               'xlm': 'stellar',
               'link': 'chainlink',
               'dot': 'polkadot',
               'yfi': 'yearn-finance'}

common_crypto = {'btc': 'bitcoin',
                 'eth': 'ethereum',
                 'bnb': 'binancecoin',
                 'usdt': 'tether',
                 'sol': 'solana',
                 'usdc': 'usd-coin',
                 'ada': 'cardano',
                 'xrp': 'ripple',
                 'dot': 'polkadot',
                 'luna': 'terra-luna',
                 'doge': 'dogecoin',
                 'avax': 'avalanche-2',
                 'shib': 'shiba-inu',
                 'matic': 'matic-network',
                 'busd': 'binance-usd',
                 'cro': 'crypto-com-chain',
                 'near': 'near',
                 'link': 'chainlink',
                 'wbtc': 'wrapped-bitcoin',
                 'atom': 'cosmos',
                 'ust': 'terrausd',
                 'ltc': 'litecoin',
                 'bch': 'bitcoin-cash',
                 'uni': 'uniswap',
                 'trx': 'tron',
                 'xlm': 'stellar',
                 'ftt': 'ftx-token',
                 'fil': 'filecoin',
                 'eos': 'eos',
                 'tusd': 'true-usd',
                 'usdp': 'paxos-standard',
                 'husd': 'husd',
                 'gusd': 'gemini-dollar',
                 'usdk': 'usdk',
                 }


def func_args_preprocessing(func):
    """Return function that converts list input arguments to comma-separated strings"""

    def input_args(*args, **kwargs):
        # check in **kwargs for lists and booleans
        for v in kwargs:
            kwargs[v] = arg_preprocessing(kwargs[v])
        # check in *args for lists and booleans
        args = [arg_preprocessing(v) for v in args]

        return func(*args, **kwargs)

    return input_args


def arg_preprocessing(arg_v):
    """Return the values of an argument after preprocessing"""

    # check if arg is list and convert it to comma-separated string
    if isinstance(arg_v, list):
        arg_v = ','.join(arg_v)
    # check if arg is boolean and convert it to string
    elif isinstance(arg_v, bool):
        arg_v = str(arg_v).lower()

    return arg_v


def get_comma_separated_values(values):
    """Return the values as a comma-separated string"""

    # Make sure values is a list or tuple
    if not isinstance(values, list) and not isinstance(values, tuple):
        values = [values]

    return ','.join(values)


class CoinGeckoAPI:
    __API_URL_BASE = 'https://api.coingecko.com/api/v3/'

    def __init__(self, api_base_url=__API_URL_BASE):
        self.api_base_url = api_base_url
        self.request_timeout = 120

        self.session = requests.Session()
        retries = Retry(total=5, backoff_factor=0.5, status_forcelist=[502, 503, 504])
        self.session.mount('http://', HTTPAdapter(max_retries=retries))

    def __request(self, url):
        # print(url)
        try:
            response = self.session.get(url, timeout=self.request_timeout)
        except requests.exceptions.RequestException:
            raise

        try:
            response.raise_for_status()
            content = json.loads(response.content.decode('utf-8'))
            return content
        except Exception as e:
            # check if json (with error message) is returned
            try:
                content = json.loads(response.content.decode('utf-8'))
                raise ValueError(content)
            # if no json
            except json.decoder.JSONDecodeError:
                pass

            raise

    def __api_url_params(self, api_url, params, api_url_has_params=False):
        if params:
            # if api_url contains already params and there is already a '?' avoid
            # adding second '?' (api_url += '&' if '?' in api_url else '?'); causes
            # issues with request parametes (usually for endpoints with required
            # arguments passed as parameters)
            api_url += '&' if api_url_has_params else '?'
            for key, value in params.items():
                if type(value) == bool:
                    value = str(value).lower()

                api_url += "{0}={1}&".format(key, value)
            api_url = api_url[:-1]
        return api_url

    # ---------- PING ----------#
    def ping(self):
        """Check API server status"""

        api_url = '{0}ping'.format(self.api_base_url)
        return self.__request(api_url)

    # ---------- SIMPLE ----------#
    @func_args_preprocessing
    def get_price(self, ids, vs_currencies, **kwargs):
        """
        获取数字货币的当前最新价，支持不同货币计价； 注意只是最新时刻的数据
        Get the current price of any cryptocurrencies in any other supported currencies that you need
        :param str ids: id of coins, comma-separated if querying more than 1 coin *refers to coins/list
        :param str vs_currencies: vs_currency of coins, comma-separated if querying more than 1 vs_currency *refers to simple/supported_vs_currencies
        :param kwargs:
            - include_market_cap: true/false to include market_cap, default: false
            - include_24hr_vol: true/false to include 24hr_vol, default: false
            - include_24hr_change: true/false to include 24hr_change, default: false
            - include_last_updated_at: true/false to include last_updated_at of price, default: false
        :return:
        """

        ids = ids.replace(' ', '')
        kwargs['ids'] = ids
        vs_currencies = vs_currencies.replace(' ', '')
        kwargs['vs_currencies'] = vs_currencies

        api_url = '{0}simple/price'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_token_price(self, id, contract_addresses, vs_currencies, **kwargs):
        """
        获取某个链上某个tokens的最新价格
        Get current price of tokens (using contract addresses) for a given platform in any other currency that you need.
        :param id: The id of the platform issuing tokens (See asset_platforms endpoint for list of options)
        :param contract_addresses: The contract address of tokens, comma separated
        :param vs_currencies: vs_currency of coins, comma-separated if querying more than 1 vs_currency *refers to simple/supported_vs_currencies
        :param kwargs:
            - include_market_cap: true/false to include market_cap, default: false
            - include_24hr_vol: true/false to include 24hr_vol, default: false
            - include_24hr_change: true/false to include 24hr_change, default: false
            - include_last_updated_at: true/false to include last_updated_at of price, default: false
        :return:
        """

        contract_addresses = contract_addresses.replace(' ', '')
        kwargs['contract_addresses'] = contract_addresses
        vs_currencies = vs_currencies.replace(' ', '')
        kwargs['vs_currencies'] = vs_currencies

        api_url = '{0}simple/token_price/{1}'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)
        return self.__request(api_url)

    @func_args_preprocessing
    def get_supported_vs_currencies(self, **kwargs):
        """
        所有的计价单位 （即包含常用法币，也包含一些较常用代币）
        ['btc', 'eth','ltc','bch','bnb','eos','xrp','xlm','link','dot','yfi',
        'usd','aed','ars','aud','bdt','bhd','bmd','brl','cad','chf','clp','cny','czk','dkk','eur','gbp','hkd','huf',
        'idr','ils','inr','jpy','krw','kwd','lkr','mmk','mxn','myr','ngn','nok','nzd','php', 'pkr','pln','rub','sar',
        'sek','sgd','thb','try','twd','uah','vef','vnd','zar','xdr','xag','xau','bits','sats']
        Get list of supported_vs_currencies
        """

        api_url = '{0}simple/supported_vs_currencies'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- COINS ----------#
    @func_args_preprocessing
    def get_coins(self, **kwargs):
        """List all coins with data (name, price, market, developer, community, etc)"""

        api_url = '{0}coins'.format(self.api_base_url)
        # ['order', 'per_page', 'page', 'localization']
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_coins_list(self, **kwargs):
        """
        列出所有支持的代币的id，名称，代码
        List all supported coins id, name and symbol (no pagination required)
        :param kwargs:
            - include_platform: flag to include platform contract addresses (eg. 0x.... for Ethereum based tokens). valid values: true, false
        :return:
                   id       symbol     name
           1    bitcoin    btc        Bitcoin
        """

        api_url = '{0}coins/list'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return pd.DataFrame(self.__request(api_url))

    @func_args_preprocessing
    def get_coins_markets(self, vs_currency, **kwargs):
        """
        获取币当前基本信息，最新价格，市值，市值排名，交易量，流通量，最大供应量等市场相关信息
        List all supported coins price, market cap, volume, and market related data
        :param vs_currency: The target currency of market data (usd, eur, jpy, etc.)
        :param kwargs:
            - ids: The ids of the coin, comma separated crytocurrency symbols (base). refers to /coins/list.
                   When left empty, returns numbers the coins observing the params limit and start
            - category: filter by coin category. Refer to /coin/categories/list
            - order: valid values: market_cap_desc, gecko_desc, gecko_asc, market_cap_asc, market_cap_desc, volume_asc, volume_desc, id_asc, id_desc
                    sort results by field. Default value : market_cap_desc
            - per_page: valid values: 1..250; Total results per page; Default value : 100
            - int page: Page through results; Default value : 1
            - boolean sparkline: Include sparkline 7 days data (eg. true, false), Default value : false
            - str price_change_percentage: Include price change percentage in 1h, 24h, 7d, 14d, 30d, 200d, 1y (eg. '1h,24h,7d' comma-separated, invalid values will be discarded)

            - bool all_data: 是否一次性获取全部币的信息，默认为False
        :return:
        """

        kwargs['vs_currency'] = vs_currency
        api_url = '{0}coins/markets'.format(self.api_base_url)
        all_data = kwargs.pop('all_data', False)
        if all_data == 'true':
            res_list = []
            page_num = 1
            Flag = True
            while Flag and (page_num < 10000):
                kwargs['page'] = page_num
                kwargs['per_page'] = 250
                api_url_kwargs = self.__api_url_params(api_url, kwargs)
                res = pd.DataFrame(self.__request(api_url_kwargs))
                if res.empty:
                    Flag = False  # 数据获取完毕
                else:
                    res_list.append(res)
                    page_num = page_num + 1
                time.sleep(1.5)
            all_df = pd.concat(res_list, ignore_index=True)
            return all_df
        else:
            api_url_kwargs = self.__api_url_params(api_url, kwargs)
            res = pd.DataFrame(self.__request(api_url_kwargs))
            return res

    @func_args_preprocessing
    def get_coin_by_id(self, id, **kwargs):
        """
        获取币的最新基本信息如：出块时间， 加密算法，类别，世纪块时间，舆情评分信息，各币种的最新价格数据及相关统计数据，
                          社区followers信息评论信息等，开发者信息，以及各交易所最新的交易信息（只显示100条记录，更详细的需要通过get_coin_ticker_by_id获取）
        Get current data (name, price, market, ... including exchange tickers) for a coin
        IMPORTANT:
        Ticker object is limited to 100 items, to get more tickers, use /coins/{id}/tickers
        Ticker is_stale is true when ticker that has not been updated/unchanged from the exchange for a while.
        Ticker is_anomaly is true if ticker's price is outliered by our system.

        :param id: pass the coin id (can be obtained from /coins) eg. bitcoin
        :param kwargs:
            - localization： Include all localized languages in response (true/false) [default: true] # 各语言下的名字翻译
            - tickers： Include tickers data (true/false) [default: true] # 是否包含各交易所最新的交易信息， 最多只包含100条，更多的数据需要get_coin_ticker_by_id获取
            - market_data：Include market_data (true/false) [default: true]
            - community_data：Include community_data data (true/false) [default: true]
            - developer_data：Include developer_data data (true/false) [default: true]
            - sparkline：Include sparkline 7 days data (eg. true, false) [default: false]
        :return:
        """

        api_url = '{0}coins/{1}/'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_coin_ticker_by_id(self, id, **kwargs):
        """
        获取最新各家交易所关于该币种的交易数据（交易对名称id信息，交易所名称，交易对价格，交易额，bid ask价差，数据是否异常等信息），每次获取100个； 可以自己拼接。
        该函数是get_coin_by_id中ticker_data的完整版

        Get coin tickers (paginated to 100 items)
        IMPORTANT:
        Ticker is_stale is true when ticker that has not been updated/unchanged from the exchange for a while.
        Ticker is_anomaly is true if ticker's price is outliered by our system.
        You are responsible for managing how you want to display these information (e.g. footnote, different background, change opacity, hide)

        :param id: pass the coin id (can be obtained from /coins/list) eg. bitcoin
        :param kwargs:
            - int exchange_ids：filter results by exchange_ids (ref: v3/exchanges/list)
            - include_exchange_logo： flag to show exchange_logo
            - int page：Page through results
            - order:valid values: trust_score_desc (default), trust_score_asc and volume_desc
            - depth:flag to show 2% orderbook depth. valid values: true, false
        :return:
        """

        api_url = '{0}coins/{1}/tickers'.format(self.api_base_url, id)

        all_data = kwargs.pop('all_data', False)
        if all_data == 'true':
            tickers_list = []
            page_num = 1
            Flag = True
            while Flag and (page_num < 100):
                kwargs['page'] = page_num
                kwargs['order'] = 'volume_desc'
                api_url_kwargs = self.__api_url_params(api_url, kwargs)
                res = self.__request(api_url_kwargs)
                if (page_num == 1):
                    tickers_list = res['tickers']
                    page_num = page_num + 1
                elif (page_num != 1) and res:
                    tickers_list.extend(res['tickers'])
                    page_num = page_num + 1
                elif not res:
                    Flag = False
                time.sleep(1)

            all_tickers = pd.DataFrame(tickers_list)
            return all_tickers
        else:
            api_url = self.__api_url_params(api_url, kwargs)
            return self.__request(api_url)

    @func_args_preprocessing
    def get_coin_history_by_id(self, id, date, **kwargs):
        """
        获取币的某天的基本信息如：对应不同法币或常用币种的的价格，市值，交易额数据； 社区followers信息评论信息等； 开发者信息
        Get historical data (name, price, market, stats) at a given date for a coin
        :param id:
        :param date:
        :param kwargs:
        :return:
        """

        kwargs['date'] = date

        api_url = '{0}coins/{1}/history'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)
        res = self.__request(api_url)
        # res = pd.DataFrame(self.__request(api_url))
        # pd.DataFrame(res)
        return res

    @func_args_preprocessing
    def get_coin_market_chart_by_id(self, id=None, symbol=None, days=None, **kwargs):
        """
        获取该币的今日至days天前的历史收盘价，市值，24小时交易量（BTC计价），交易额（usd）

        Get historical market data include price, market cap, and 24h volume (granularity auto)
        Minutely data will be used for duration within 1 day,
        Hourly data will be used for duration between 1 day and 90 days,
        Daily data will be used for duration above 90 days.

        :param symbol: 币简称，原始接口需要输入的是id， 但是id不好记，所以输入symbol，下面会自动转换为id
        :param days: Data up to number of days ago (eg. 1,14,30,max)
        :param kwargs:
            - vs_currency: The target currency of market data (usd, eur, jpy, etc.)
            - interval: Data interval. Possible value: daily
        :return:
        """
        if id is None:
            id = common_crypto[symbol]
        vs_currency = 'usd'
        api_url = '{0}coins/{1}/market_chart?vs_currency={2}&days={3}'.format(self.api_base_url, id, vs_currency, days)
        api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
        res_usd = self.__request(api_url)

        prices_df = pd.DataFrame(res_usd['prices'], columns=['date', 'close']).set_index('date')
        market_caps_df = pd.DataFrame(res_usd['market_caps'], columns=['date', 'market_cap']).set_index('date')
        amount_df = pd.DataFrame(res_usd['total_volumes'], columns=['date', 'amount']).set_index('date')

        if symbol in fiat_crypto.keys():
            vs_currency = symbol
            api_url = '{0}coins/{1}/market_chart?vs_currency={2}&days={3}'.format(self.api_base_url, id, vs_currency, days)
            api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
            res_btc = self.__request(api_url)
            volumes_df = pd.DataFrame(res_btc['total_volumes'], columns=['date', 'volume']).set_index('date')
            all_df = pd.concat([prices_df, market_caps_df, amount_df, volumes_df], axis=1)
        else:
            all_df = pd.concat([prices_df, market_caps_df, amount_df], axis=1)

        all_df.reset_index(inplace=True)
        all_df['date'] = pd.to_datetime(all_df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert('Asia/Shanghai')
        # all_df['date'] = pd.to_datetime(all_df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert('Europe/London')
        all_df['date'] = all_df['date'].astype(str)
        all_df = all_df[all_df['date'].str.contains('08:00:00')]  # 最新的一天数据还不完整，剔除掉
        all_df['date'] = all_df['date'].str[:10]
        all_df = all_df.set_index('date')
        all_df = all_df.shift(-1)
        all_df = all_df.iloc[:-1, :]
        return all_df

    @func_args_preprocessing
    def get_coin_market_chart_range_by_id(self, id=None, symbol=None, start_date=None, end_date=None, **kwargs):
        """
        获取该币的start_date到end_date之间的收盘价，市值，24小时交易量（BTC计价），交易额（usd）， 这里比上面的函数更好用些
        # 注意 2015-01-28的数据有缺失
        Get historical market data include price, market cap, and 24h volume within a range of timestamp (granularity auto)
        Data granularity is automatic (cannot be adjusted)
        1 day from query time = 5 minute interval data
        1 - 90 days from query time = hourly data
        above 90 days from query time = daily data (00:00 UTC)

        :param id: 币简称，原始接口需要输入的是id
        :param symbol: 币简称，原始接口需要输入的是id， 但是id不好记，所以输入symbol，下面会自动转换为id
        :param start_date: 格式为‘2015-01-01’， 代码里面需要转换为From date in UNIX Timestamp (eg. 1392577232)
        :param end_date: 格式为‘2015-01-01’， 代码里面需要转换为To date in UNIX Timestamp (eg. 1422577232)
        :param kwargs:
        :return:
        """

        if id is None:
            id = common_crypto[symbol]
        print(f'开始通过coingecko获取{id}的收盘价，市值，交易量，交易额')
        vs_currency = 'usd'
        from_timestamp = str_to_timestamp(start_date, tz_str='+0000')
        to_timestamp = str_to_timestamp(end_date, tz_str='+0000')
        to_timestamp = to_timestamp + 86400  # 多增加一天，因为end_date的收盘数据其实是对应的下一天的00:00:00

        try:
            api_url = '{0}coins/{1}/market_chart/range?vs_currency={2}&from={3}&to={4}'.format(self.api_base_url, id, vs_currency, from_timestamp, to_timestamp)
            api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
            res_usd = self.__request(api_url)
        except Exception as err:
            if 'Could not find coin with the given id' in str(err):
                print(f'Could not find coin with the given id {id}')
            return None

        prices_df = pd.DataFrame(res_usd['prices'], columns=['end_date', 'close']).set_index('end_date')
        market_caps_df = pd.DataFrame(res_usd['market_caps'], columns=['end_date', 'market_cap']).set_index('end_date')
        amount_df = pd.DataFrame(res_usd['total_volumes'], columns=['end_date', 'amount']).set_index('end_date')

        # 获取交易量的数据
        if symbol in fiat_crypto.keys():
            vs_currency = symbol
            api_url = '{0}coins/{1}/market_chart/range?vs_currency={2}&from={3}&to={4}'. \
                format(self.api_base_url, id, vs_currency, from_timestamp, to_timestamp)
            api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)
            res_btc = self.__request(api_url)
            volumes_df = pd.DataFrame(res_btc['total_volumes'], columns=['end_date', 'volume']).set_index('end_date')
        else:
            volumes_df = amount_df['amount'] / prices_df['close']
            volumes_df.name = 'volume'
        all_df = pd.concat([prices_df, market_caps_df, amount_df, volumes_df], axis=1)

        all_df.reset_index(inplace=True)
        all_df['end_date'] = pd.to_datetime(all_df['end_date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert('+0000')
        all_df['end_date'] = all_df['end_date'].astype(str)
        all_df['end_date'] = all_df['end_date'].str[:10]
        all_df = all_df.set_index('end_date')
        all_df = all_df.shift(-1)  # 这里的日期对应的是00:00:00，其实是前一天的收盘信息，所以需要shift一下
        all_df = all_df.iloc[:-1, :]  # shift完，剔除掉最后一行

        if symbol == 'btc':
            # 注意 btc 的 2015-01-28的数据有缺失, 直接填充为27号的数据
            all_df.loc['2015-01-28', :] = all_df.loc['2015-01-27', :]
            all_df.sort_index(ascending=True, inplace=True)

        print(f'通过coingecko获取{id}的收盘价，市值，交易量，交易额 成功')

        return all_df

    @func_args_preprocessing
    def get_coin_status_updates_by_id(self, id=None, symbol=None, **kwargs):
        """
        关于该币的最新的协议修改，重大新闻等信息
        Get status updates for a given coin
        :param symbol:
        :param kwargs:
        :return:
        """

        if id is None:
            id = common_crypto[symbol]
        api_url = '{0}coins/{1}/status_updates'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)
        res = self.__request(api_url)
        return res

    @func_args_preprocessing
    def get_coin_ohlc_by_id(self, id=None, symbol=None, days=None, **kwargs):
        """
        # 数据质量可能有点问题， 部分数据的前一条收盘跟下一条的开盘数据相差较大
        # 目前数据不支持日K线，需要日K线的话，需要根据四小时线进行组合

        Get coin's OHLC
        Candle's body:
        1 - 2 days: 30 minutes
        3 - 30 days: 4 hours
        31 and before: 4 days

        :param symbol:
        :param vs_currency:
        :param days:
        :param kwargs:
        :return:
        """
        if id is None:
            id = common_crypto[symbol]
        vs_currency = 'usd'
        api_url = '{0}coins/{1}/ohlc?vs_currency={2}&days={3}'.format(self.api_base_url, id, vs_currency, days)
        api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)

        res = self.__request(api_url)
        ohlc_df = pd.DataFrame(res, columns=['date', 'open', 'high', 'low', 'close'])
        ohlc_df['date'] = pd.to_datetime(ohlc_df['date'], unit='ms').dt.tz_localize('UTC').dt.tz_convert(tz='+0000')
        ohlc_df['date'] = ohlc_df['date'].astype(str)
        ohlc_df['date'] = ohlc_df['date'].str[:19]
        ohlc_df = ohlc_df.set_index('date')
        ohlc_df = ohlc_df.shift(-1)
        ohlc_df = ohlc_df.iloc[:-1, :]
        return ohlc_df

    # ---------- Contract ----------#
    @func_args_preprocessing
    def get_coin_info_from_contract_address_by_id(self, id, contract_address, **kwargs):
        """Get coin info from contract address"""

        api_url = '{0}coins/{1}/contract/{2}'.format(self.api_base_url, id, contract_address)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_coin_market_chart_from_contract_address_by_id(self, id, contract_address, vs_currency, days, **kwargs):
        """Get historical market data include price, market cap, and 24h volume (granularity auto) from a contract address"""

        api_url = '{0}coins/{1}/contract/{2}/market_chart/?vs_currency={3}&days={4}'.format(self.api_base_url, id,
                                                                                            contract_address,
                                                                                            vs_currency, days)
        api_url = self.__api_url_params(api_url, kwargs, api_url_has_params=True)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_coin_market_chart_range_from_contract_address_by_id(self, id, contract_address, vs_currency, from_timestamp,
                                                                to_timestamp, **kwargs):
        """Get historical market data include price, market cap, and 24h volume within a range of timestamp (granularity auto) from a contract address"""

        api_url = '{0}coins/{1}/contract/{2}/market_chart/range?vs_currency={3}&from={4}&to={5}'.format(
            self.api_base_url, id, contract_address, vs_currency, from_timestamp, to_timestamp)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- ASSET PLATFORMS ----------#
    @func_args_preprocessing
    def get_asset_platforms(self, **kwargs):
        """List all asset platforms (Blockchain networks)"""

        api_url = '{0}asset_platforms'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- CATEGORIES ----------#
    @func_args_preprocessing
    def get_coins_categories_list(self, **kwargs):
        """List all categories"""

        api_url = '{0}coins/categories/list'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_coins_categories(self, **kwargs):
        """List all categories with market data"""

        api_url = '{0}coins/categories'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- EXCHANGES ----------#
    @func_args_preprocessing
    def get_exchanges_list(self, **kwargs):
        """
        List all exchanges: 包括中心化交易所，和去中心化交易所

        返回数据包含： id, name, year_established, country, description, url, image, has_trading_incentive, trust_score, trust_score_rank,
                      trade_volume_24h_btc, trade_volume_24h_btc_normalized

        """

        api_url = '{0}exchanges'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        res = self.__request(api_url)
        res = pd.DataFrame(res)
        return res

    @func_args_preprocessing
    @deco_retry(retry=50, retry_sleep=15)
    def get_exchanges_id_name_list(self, **kwargs):
        """
        List all supported markets id and name (no pagination required)

        所有交易所的id, name
        """

        api_url = '{0}exchanges/list'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        res = self.__request(api_url)
        res = pd.DataFrame(res)
        return res

    @func_args_preprocessing
    def get_exchanges_by_id(self, id=None, **kwargs):
        """
        获取交易所详细信息

        返回信息包括： name, year_established, country, description, url, image, facebook_url, reddit_url, telegram_url, slack_url, other_url
                      has_trading_incentive, centralized, public_notice, alert_notice, trust_score, trust_score_rank,
                      trade_volume_24h_btc, trade_volume_24h_btc_normalized,
                      tickers[每个交易对的信息，包含价格，交易额，bid_ask_spread，等信息]
        """
        if id is not None:
            api_url = '{0}exchanges/{1}'.format(self.api_base_url, id)
            api_url = self.__api_url_params(api_url, kwargs)
            res = self.__request(api_url)
        else:
            all_ids = self.get_exchanges_id_name_list()['id']
            res_list = []
            for id in all_ids:
                api_url = '{0}exchanges/{1}'.format(self.api_base_url, id)
                api_url = self.__api_url_params(api_url, kwargs)
                res = self.__request(api_url)
                res_list.append(res)
                time.sleep(3)
            res = pd.DataFrame(res_list)
        return res

    @func_args_preprocessing
    def get_exchanges_tickers_by_id(self, id, **kwargs):
        """
        Get exchange tickers (paginated, 100 tickers per page)

        返回消息： name, tickers[每个交易对的信息，包含价格，交易额，bid_ask_spread，等信息]
        """

        api_url = '{0}exchanges/{1}/tickers'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)

        res = self.__request(api_url)
        return res

    @func_args_preprocessing
    def get_exchanges_status_updates_by_id(self, id, **kwargs):
        """Get status updates for a given exchange"""

        api_url = '{0}exchanges/{1}/status_updates'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)

        res = self.__request(api_url)
        return res

    @func_args_preprocessing
    @deco_retry(retry=50, retry_sleep=5)
    def get_exchanges_volume_chart_by_id(self, id=None, days=1500, **kwargs):
        """
        获取交易所现货交易额，单位为BTC（该代码已经转换为USD了）；todo 注意这里得到的是未剔除异常交易的总交易额，无法获取剔除异常交易额之后的总交易额

        Get volume chart data for a given exchange
        :param id:
        :param days: 1/7/14/30/90/180/365 只能是这些参数
        :param kwargs:
        :return:
        """

        kwargs['days'] = days
        api_url = '{0}exchanges/{1}/volume_chart'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)

        res = self.__request(api_url)
        res_df = pd.DataFrame(res, columns=['timestamp', 'amount'])

        res_df['date'] = pd.to_datetime(res_df['timestamp'], unit='ms').dt.tz_localize('UTC').dt.tz_convert(tz='+0000')
        res_df['date'] = res_df['date'].astype(str)
        res_df['date'] = res_df['date'].str[:10]
        res_df = res_df.set_index('date')

        # exchange_rate = float(self.get_exchange_rates()['rates']['usd']['value'])
        res_df['amount'] = res_df['amount'].astype(float)
        # res_df['amount'] = res_df['amount'] * exchange_rate

        return res_df

    @func_args_preprocessing
    @deco_retry(retry=50, retry_sleep=5)
    def get_exchanges_volume_chart_by_id_v2(self, id=None, days=1500, **kwargs):
        kwargs['days'] = days
        api_url = '{0}exchanges/{1}/volume_chart'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)

        res = self.__request(api_url)
        res_df = pd.DataFrame(res, columns=['timestamp', 'amount'])

        res_df['date'] = pd.to_datetime(res_df['timestamp'], unit='ms').dt.tz_localize('UTC').dt.tz_convert(tz='+0000')
        res_df = res_df.set_index('date')
        res_df['amount'] = res_df['amount'].astype(float)
        amount = res_df['amount']
        return amount


    @deco_retry(retry=50, retry_sleep=5)
    def get_derivative_exchanges_volume_by_id(self, id=None, days=1500, **kwargs):
        """
        获取衍生品交易所交易额，最多只能获取近一年数据；

        Get volume chart data for a given exchange
        :param id:  支持的交易所id 包含['binance', 'okex', 'bybit', 'ftx', 'kucoin', 'crypto_com', 'huobi', 'gate', 'bitfinex', 'kraken', 'bitmex']
        :return:
        """
        exchange_id_num_dict = {'binance': 466, 'okex': 379, 'bybit': 460, 'ftx': 443, 'kucoin': 471, 'bitmex': 378,
                                'crypto_com': 669, 'huobi': 433, 'gate': 403, 'bitfinex': 486, 'kraken': 426}
        exchange_id_num = exchange_id_num_dict[id]
        # header = {
        #     # 'accept-encoding': 'gzip, deflate, br',
        #     'cookie': '__gads=ID=17d3ec92ba471d76:T=1675304717:S=ALNI_MY5Mp1wVcsjl0qkEg7Ox0TbwT3d3Q; cf_clearance=LKvZhBMkiFsfAHLak6NuHx_pr2F3xyBHcoOgp6d73JI-1676273183-0-150; __cf_bm=xidagNdakJAI2B2QfMLslsYJz1wilcyNRc6A.NuFXRs-1683256279-0-AUtwXxlieX+wOyMW4FaaHQ6xaMrtrPro9wAPdXVSbtobmJ8JR6Pbr4Ncs42zPQ0GZ2+0qAFPSWHDAhVVA69+7dY=; _gcl_au=1.1.74206938.1683256280; _gaexp=GAX1.2.9hRYpLC0RCyyNA62gz8SFA.19524.x538; geckoTableFdvStats=false; indexCategoryCardTrend=true; _session_id=9779a5dea121236a42344a29fd609b6e; _gid=GA1.2.827279856.1683256282; __gpi=UID=00000bb3318edbb7:T=1675304717:RT=1683256282:S=ALNI_MYN5RQJAq0Wa7EVjVTL_OfFptuqhQ; cookie_notice_accept=1; _ga_LJR3232ZPB=GS1.1.1683256279.8.1.1683256341.0.0.0; _ga=GA1.2.1859636205.1675304716; _gat_gtag_UA_49392197_1=1; datadome=6SPrr0kp07jIvmiGQsCAb8LUOe91YcPLr4qHZ7DrDx4aJoz6n2NgX4ASmj73KpNKhMza92zhMpfPMDESEqT2sQXuQeXIE5eh5ziULWTts8xo_iXcE82XtKx79xaE0ESY',
        #     'sec-ch-ua': '"Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"',
        #     'sec-ch-ua-platform': '"Windows"',
        #     'referer': 'https://www.coingecko.com/en/exchanges/binance_futures',
        #     'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36', }
        # # url = f"https://www.coingecko.com/exchanges/{exchange_id_num}/usd/1_year.json?locale=en"
        # url = f"https://www.coingecko.com/exchanges/{exchange_id_num}/usd/30_days.json?locale=en"
        # res = requests.get(url, headers=header, timeout=60)
        # res = json.loads(res.text)
        #
        # volume_df = pd.DataFrame(res['volumes'], columns=['timestamp', 'amount'])
        # open_interest_df = pd.DataFrame(res['open_interest'], columns=['timestamp', 'open_interest'])
        # res_df = volume_df.merge(open_interest_df, on='timestamp')
        #
        # res_df['date'] = pd.to_datetime(res_df['timestamp'], unit='ms').dt.tz_localize('UTC').dt.tz_convert(tz='+0000')
        # res_df['date'] = res_df['date'].astype(str)
        # res_df['date'] = res_df['date'].str[:10]
        # res_df = res_df.set_index('date')
        #
        # res_df = res_df.astype(float)

        res_df = self.get_exchanges_volume_chart_by_id(id=exchange_id_num, days=days)

        return res_df

    @deco_retry(retry=50, retry_sleep=15)
    def get_total_marketcap_volume(self, ):
        """
        获取coingecko全市场总现货交易额，最多只能获取近一年数据；

        :return:
        """
        header = {
            'accept': '*/*',
            'accept-encoding': 'gzip, deflate, br',
            'accept-language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh-TW;q=0.7,zh;q=0.6',
            'cookie': 'cf_clearance=LKvZhBMkiFsfAHLak6NuHx_pr2F3xyBHcoOgp6d73JI-1676273183-0-150; cookie_notice_accept=1; _gaexp=GAX1.2.wdQfiN-rQT6nmgQxbaKaCA.19672.1; _gcl_au=1.1.755368527.1695361382; _cc_id=630231e81f1c65b11ae76f1d68a7df1d; __gads=ID=17d3ec92ba471d76:T=1675304717:RT=1695361383:S=ALNI_MY5Mp1wVcsjl0qkEg7Ox0TbwT3d3Q; __gpi=UID=00000bb3318edbb7:T=1675304717:RT=1695361383:S=ALNI_MYN5RQJAq0Wa7EVjVTL_OfFptuqhQ; _au_1d=AU1D-0100-001695361384-PXPCPB26-F6IN; datadome=5Xm4-s1qE2Ygw346i7tt7Q7slE_YGO5hE6fsPeFKn5Wis-b1TRsmmWwe3fJuUfQa982PsR2B~8yn8uCbht7HDFR-vHJdUTVH1roN~luriJBCQ5MlIcER17SduFQbsAZ7; __cf_bm=s6vG9tB4rnbBZuB_s4iSQc2TIu3443QCiBxsoahZMzI-1695697178-0-AV4y1QhxCQ2B2pnnqLC4nYvkeFQ+dlzC5KyBR8exceOJH8H5mb1xB1XqSf8nEJ0GLFHb7x8DTp8kmRH4vMKgWIo=; panoramaId_expiry=1696301979805; panoramaId=49801f93d6b31d2d3921e9f19f2616d539382a63651a3726339de38d3ac5e8c0; panoramaIdType=panoIndiv; _session_id=264a3472842409e7385155340b0c8d7a; _au_last_seen_pixels=eyJhcG4iOjE2OTU2OTcxODEsInR0ZCI6MTY5NTY5NzE4MSwicHViIjoxNjk1Njk3MTgxLCJydWIiOjE2OTU2OTcxODEsInRhcGFkIjoxNjk1Njk3MTgxLCJhZHgiOjE2OTU2OTcxODEsImdvbyI6MTY5NTY5NzE4MSwidW5ydWx5IjoxNjk1MzYxMzg0LCJzb24iOjE2OTUzNjEzODQsImluZGV4IjoxNjk1MzYxMzg0LCJzbWFydCI6MTY5NTY5NzE4MSwiYmVlcyI6MTY5NTM2MTM5MiwiY29sb3NzdXMiOjE2OTUzNjEzOTIsImFtbyI6MTY5NTY5NzE4MSwib3BlbngiOjE2OTUzNjEzOTIsImltcHIiOjE2OTUzNjEzOTIsInBwbnQiOjE2OTUzNjEzOTIsInRhYm9vbGEiOjE2OTUzNjEzOTIsImFkbyI6MTY5NTY5NzE4MX0%3D; _gid=GA1.2.1477694012.1695697181; _ga_LJR3232ZPB=GS1.1.1695697178.12.1.1695697192.0.0.0; _ga=GA1.1.1859636205.1675304716',
            'referer': 'https://www.coingecko.com/en/global-charts',
            'Sec-Ch-Device-Memory': '8',
            'sec-ch-ua': '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"',
            'Sec-Ch-Ua-Arch': '"x86"',
            'Sec-Ch-Ua-Full-Version-List': '"Google Chrome";v="117.0.5938.89", "Not;A=Brand";v="8.0.0.0", "Chromium";v="117.0.5938.89"',
            'sec-ch-ua-mobile': '?0',
            'Sec-Ch-Ua-Model': '""',
            'sec-ch-ua-platform': '"Windows"',
            'sec-fetch-dest': 'empty',
            'sec-fetch-mode': 'cors',
            'sec-fetch-site': 'same-origin',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'}
        url = f"https://www.coingecko.com/market_cap/total_charts_data?locale=en&vs_currency=usd"
        res = requests.get(url, headers=header, timeout=60)
        res = json.loads(res.text)

        volume_df = pd.DataFrame(res['volumes'], columns=['timestamp', 'amount'])
        open_interest_df = pd.DataFrame(res['open_interest'], columns=['timestamp', 'open_interest'])
        res_df = volume_df.merge(open_interest_df, on='timestamp')

        res_df['date'] = pd.to_datetime(res_df['timestamp'], unit='ms').dt.tz_localize('UTC').dt.tz_convert(tz='+0000')
        res_df['date'] = res_df['date'].astype(str)
        res_df['date'] = res_df['date'].str[:10]
        res_df = res_df.set_index('date')

        res_df = res_df.astype(float)

        return res_df

    # ---------- FINANCE ----------#
    @func_args_preprocessing
    def get_finance_platforms(self, **kwargs):
        """Get cryptocurrency finance platforms data"""

        api_url = '{0}finance_platforms'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_finance_products(self, **kwargs):
        """Get cryptocurrency finance products data"""

        api_url = '{0}finance_products'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- INDEXES ----------#
    @func_args_preprocessing
    def get_indexes(self, **kwargs):
        """List all market indexes"""

        api_url = '{0}indexes'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # @func_args_preprocessing
    # def get_indexes_by_id(self, id, **kwargs):
    #    """Get market index by id"""
    #
    #    api_url = '{0}indexes/{1}'.format(self.api_base_url, id)
    #    api_url = self.__api_url_params(api_url, kwargs)
    #
    #    return self.__request(api_url)

    @func_args_preprocessing
    def get_indexes_by_market_id_and_index_id(self, market_id, id, **kwargs):
        """Get market index by market id and index id"""

        api_url = '{0}indexes/{1}/{2}'.format(self.api_base_url, market_id, id)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_indexes_list(self, **kwargs):
        """List market indexes id and name"""

        api_url = '{0}indexes/list'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- DERIVATIVES ----------#
    @func_args_preprocessing
    def get_derivatives(self, **kwargs):
        """
        获取当前所有衍生品的最新相关信息，包含价格，24h涨跌幅，合约类型，基差，报价基点，资金费率，开仓量，24h交易量, 最近交易时间，合约交割时间

        List all derivative tickers
        :param kwargs:
        :return:
        """

        api_url = '{0}derivatives'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)
        res = self.__request(api_url)
        all_df = pd.DataFrame(res)
        return all_df

    @func_args_preprocessing
    def get_derivatives_exchanges(self, **kwargs):
        """"
        获取当前交易所衍生品最新汇总统计信息，包含合约总开仓量，24h成交量，永续合约对数，总合约对个数
        """
        """List all derivative tickers"""

        api_url = '{0}derivatives/exchanges'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)
        res = self.__request(api_url)
        all_df = pd.DataFrame(res)
        return all_df

    @func_args_preprocessing
    def get_derivatives_exchanges_by_id(self, id, **kwargs):
        """List all derivative tickers"""

        api_url = '{0}derivatives/exchanges/{1}'.format(self.api_base_url, id)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_derivatives_exchanges_list(self, **kwargs):
        """List all derivative tickers"""

        api_url = '{0}derivatives/exchanges/list'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- STATUS UPDATES ----------#
    @func_args_preprocessing
    def get_status_updates(self, **kwargs):
        """List all status_updates with data (description, category, created_at, user, user_title and pin)"""

        api_url = '{0}status_updates'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- EVENTS ----------#
    @func_args_preprocessing
    def get_events(self, **kwargs):
        """Get events, paginated by 100"""

        api_url = '{0}events'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_events_countries(self, **kwargs):
        """Get list of event countries"""

        api_url = '{0}events/countries'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    @func_args_preprocessing
    def get_events_types(self, **kwargs):
        """Get list of event types"""

        api_url = '{0}events/types'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- EXCHANGE-RATES ----------#
    @func_args_preprocessing
    def get_exchange_rates(self, **kwargs):
        """Get BTC-to-Currency exchange rates"""

        api_url = '{0}exchange_rates'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- TRENDING ----------#
    @func_args_preprocessing
    def get_search_trending(self, **kwargs):
        """Get top 7 trending coin searches"""

        api_url = '{0}search/trending'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)

    # ---------- GLOBAL ----------#
    @func_args_preprocessing
    def get_global(self, **kwargs):
        """
        各币的最新市值，成交量，市值占比
        :param kwargs:
        :return:
        """
        """Get cryptocurrency global data"""

        api_url = '{0}global'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)['data']

    @func_args_preprocessing
    def get_global_decentralized_finance_defi(self, **kwargs):
        """Get cryptocurrency global decentralized finance(defi) data"""

        api_url = '{0}global/decentralized_finance_defi'.format(self.api_base_url)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)['data']

    # ---------- COMPANIES ----------#
    @func_args_preprocessing
    def get_companies_public_treasury_by_coin_id(self, coin_id, **kwargs):
        """
        各机构公司持有的币的量
        Get public companies data

        :param coin_id: bitcoin or ethereum
        :return:

        """

        api_url = '{0}companies/public_treasury/{1}'.format(self.api_base_url, coin_id)
        api_url = self.__api_url_params(api_url, kwargs)

        return self.__request(api_url)


# # 下面生成简称和全称的对应关系有误，因为很多简称是重复的
# cg = CoinGeckoAPI()
# common_crypto_df = cg.get_coins_list()
# common_crypto_df.set_index('symbol', inplace=True)
# common_crypto_df.sort_index(inplace=True)
# common_crypto = common_crypto_df['id'].to_dict()


if __name__ == '__main__':
    cg = CoinGeckoAPI()
    # Check API server status
    # aa = cg.ping()

    # simple
    # # Get the current price of any cryptocurrencies in any other supported currencies that you need
    # aa = cg.get_price(ids='bitcoin', vs_currencies='usd', include_market_cap=True, include_24hr_vol=True,
    #                   include_24hr_change=True, include_last_updated_at=True)
    # # Get current price of tokens (using contract addresses) for a given platform in any other currency that you need
    # aa = cg.get_token_price(id='ethereum', contract_addresses='0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce',
    #                         vs_currencies='usd')  # shib的价格
    # aa = cg.get_price(ids='shiba-inu', vs_currencies='btc')  # shib的价格
    # # Get list of supported_vs_currencies
    # aa = cg.get_supported_vs_currencies()

    # coins
    # List all supported coins id, name and symbol (no pagination required)
    # bb = cg.get_coins_list()
    # List all supported coins price, market cap, volume, and market related data
    # bb = cg.get_coins_markets(vs_currency='usd', per_page=250, page=3, all_data=False)
    # Get current data (name, price, market, ... including exchange tickers) for a coin
    # bb = cg.get_coin_by_id(id='bitcoin')
    # Get coin tickers(paginated to 100 items))
    # bb = cg.get_coin_ticker_by_id(id='binancecoin', all_data=True)
    # Get historical data(name, price, market, stats) at a given date for a coin)
    # bb = cg.get_coin_history_by_id(id='bitcoin', date='30-12-2021')
    # Get historical market data include price, market cap, and 24 h volume(granularity auto))
    # bb = cg.get_coin_market_chart_by_id(symbol='btc', days=3000, interval='daily')
    # Get historical market data include price, market cap, and 24 h volume within a range of timestamp(granularity auto))
    # bb = cg.get_coin_market_chart_range_by_id(id='bitcoin', start_date='2015-01-01', end_date='2022-01-15')
    # bb = cg.get_coin_market_chart_range_by_id(symbol='btc', start_date='2015-01-01', end_date='2022-01-15')
    # Get status updates for a given coin (beta))
    # cg.get_coin_status_updates_by_id(symbol='bnb')
    # Get coin 's OHLC (beta))
    # cg.get_coin_ohlc_by_id(symbol='btc', days=30)

    ## contract (有待测试)
    # Get coin info from contract address)
    # cg.get_coin_info_from_contract_address_by_id()
    # Get historical market data include price, market cap, and 24h volume (granularity auto) from a contract address)
    # cg.get_coin_market_chart_from_contract_address_by_id()
    # Get historical market data include price, market cap, and 24 h volume within a range of timestamp(granularity auto) from a contract address)
    # cg.get_coin_market_chart_range_from_contract_address_by_id()

    ## asset_platforms
    # List all asset platforms(Blockchain networks))
    # dd = cg.get_asset_platforms()

    ## categories
    # List all categories)
    # cc = cg.get_coins_categories_list()
    # List all categories with market data)
    # cc = cg.get_coins_categories()

    ## exchanges
    # List all exchanges)
    # dd = cg.get_exchanges_list()
    # List all supported markets id and name (no pagination required))
    # dd = cg.get_exchanges_id_name_list()
    # Get exchange volume in BTC and top 100 tickers only)
    # dd = cg.get_exchanges_by_id(id='binance')
    # Get exchange tickers (paginated, 100 tickers per page))
    # cc = cg.get_exchanges_tickers_by_id(id='binance', coin_ids='bitcoin')
    # Get status updates for a given exchange (beta))
    # cc = cg.get_exchanges_status_updates_by_id(id='binance')
    # Get volume_chart data for a given exchange (beta))
    dd = cg.get_exchanges_volume_chart_by_id(id='uniswap_v3', days=365)
    # 获取衍生品交易所近一年交易额
    cg.get_derivative_exchanges_volume_by_id(id='binance', days=30)

    ## finance
    # List all finance platforms)
    # ee = cg.get_finance_platforms()
    # List all finance products)
    # ee = cg.get_finance_products()

    ## indexes
    # List all market indexes)
    # ff = cg.get_indexes()
    # Get market index by market id and index id)
    # ff = cg.get_indexes_by_market_id_and_index_id()
    # List market indexes id and name)
    # ff = cg.get_indexes_list()

    ## derivatives
    # List all derivative tickers)
    # jj = cg.get_derivatives()
    # List all derivative exchanges)
    # jj = cg.get_derivatives_exchanges()
    # Show derivative exchange data)
    # jj = cg.get_derivatives_exchanges_by_id(id='bimex')  # 测试报错
    # List all derivative exchanges name and identifier)
    # jj = cg.get_derivatives_exchanges_list()

    ## status_updates
    # List all status_updates with data (description, category, created_at, user, user_title and pin))
    # hh = cg.get_status_updates()

    ## events
    # Get events, paginated by 100)
    # ii = cg.get_events()
    # Get list of event countries)
    # ii = cg.get_events_countries()
    # Get list of events types)
    # ii = cg.get_events_types()

    ## exchange_rates
    # Get BTC-to-Currency exchange rates)
    # jj = cg.get_exchange_rates()

    ## trending
    # Get trending search coins (Top-7) on CoinGecko in the last 24 hours)
    # kk = cg.get_search_trending()

    ## global
    # Get cryptocurrency global data)
    # ll = cg.get_global()
    # Get cryptocurrency global decentralized finance(defi) data)
    # ll = cg.get_global_decentralized_finance_defi()

    ## companies (beta)
    # Get public companies data)
    # mm = cg.get_companies_public_treasury_by_coin_id(coin_id='bitcoin')
